#include <os/binsem.h>
#include <os/lock.h>
#include <os/sched.h>
#include <os/string.h>

mutex_lock_t binsems[BINSEMS];
cond_t conds[CONDS];
barrier_t barriers[BARRIERS];
kmailbox_t mailboxs[BOXS];

void init_binsems()
{
    for (int i = 0; i < BINSEMS; ++i) {
        do_mutex_lock_init(&binsems[i]);
    }
}

static int binsem_hash(uint64_t x)
{
    // a simple hash function
    x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ul;
    x = (x ^ (x >> 27)) * 0x94d049bb133111ebul;
    x = x ^ (x >> 31);
    return x % BINSEMS;
}

int get_binsem(int key){
    return binsem_hash(key);
}

int op_binsem(int binsem_id, int op){
    if(op == BINSEM_OP_LOCK){
        do_mutex_lock_acquire(&(binsems[binsem_id]));
        current_running->binsem_id = binsem_id;
    }
    else if(op == BINSEM_OP_UNLOCK){
        do_mutex_lock_release(&(binsems[binsem_id]));
        current_running->binsem_id = -1;
    }
    else
        return 0;
    return 1;
}

void init_conds()
{
    for (int i = 0; i < CONDS; ++i){
        conds[i].num_waiting = 0;
        init_list_head(&(conds[i].list));
    }
}

static int cond_hash(uint64_t x)
{
    // a simple hash function
    x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ul;
    x = (x ^ (x >> 27)) * 0x94d049bb133111ebul;
    x = x ^ (x >> 31);
    return x % CONDS;
}

int get_cond(int key){
    return cond_hash(key);
}

int op_cond(int cond_id, int op){
    if(op == COND_OP_WAIT){
        conds[cond_id].num_waiting++;
        do_block(&(current_running->list), &(conds[cond_id].list));
    }
    else if(op == COND_OP_SIGNAL){
        if(conds[cond_id].num_waiting){
            do_unblock(conds[cond_id].list.next);
            conds[cond_id].num_waiting--;
        }
    }
    else if(op == COND_OP_BROADCAST){
        while(conds[cond_id].num_waiting){
            do_unblock(conds[cond_id].list.next);
            conds[cond_id].num_waiting--;
        }
    }
    else
        return 0;
    return 1;
}

void init_barriers()
{
    for (int i = 0; i < BARRIERS; ++i){
        init_list_head(&(barriers[i].list));
        barriers[i].num_reached = 0;
    }
}

static int barrier_hash(uint64_t x)
{
    // a simple hash function
    x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ul;
    x = (x ^ (x >> 27)) * 0x94d049bb133111ebul;
    x = x ^ (x >> 31);
    return x % BARRIERS;
}

int get_barrier(int key, unsigned count){
    int barrier_id;
    barrier_id = barrier_hash(key);
    barriers[barrier_id].num_waiting = count;
    return barrier_id;
}

int op_barrier(int barrier_id){
    barriers[barrier_id].num_reached++;
    if(barriers[barrier_id].num_reached == barriers[barrier_id].num_waiting){
        while(!list_empty(&(barriers[barrier_id].list)))
            do_unblock(barriers[barrier_id].list.next);
        barriers[barrier_id].num_reached = 0;
    }
    else
        do_block(&(current_running->list), &(barriers[barrier_id].list));
    return 1;
}

void send_mailbox(int mailbox, void *msg, int msg_length){
    if(mailboxs[mailbox].count == 0)
        kmemcpy(mailboxs[mailbox].mails[0], msg, msg_length);
    else if(mailboxs[mailbox].count == 1)
        kmemcpy(mailboxs[mailbox].mails[1], msg, msg_length);
    mailboxs[mailbox].count++;
}

void recv_mailbox(int mailbox, void *msg, int msg_length){
    if(mailboxs[mailbox].count == 1)
        kmemcpy(msg, mailboxs[mailbox].mails[0], msg_length);
    else if(mailboxs[mailbox].count == 2)
        kmemcpy(msg, mailboxs[mailbox].mails[1], msg_length);
    mailboxs[mailbox].count--;
}